home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / CompositeView.java < prev    next >
Text File  |  1998-06-30  |  13KB  |  424 lines

  1. /*
  2.  * @(#)CompositeView.java    1.29 98/04/09
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing.text;
  21.  
  22. import java.util.Vector;
  23. import java.awt.*;
  24. import com.sun.java.swing.event.*;
  25.  
  26. /**
  27.  * A view of a text model that has a children
  28.  * box.  If the box is vertical, it might be useful to represent
  29.  * something like a collection of lines or paragraphs.  If the
  30.  * box is horizontal, it might be used to represent unwrapped
  31.  * lines.
  32.  *
  33.  * @author  Timothy Prinzing
  34.  * @version 1.29 04/09/98
  35.  */
  36. public abstract class CompositeView extends View {
  37.  
  38.     /**
  39.      * Constructs a CompositeView for the given element.
  40.      *
  41.      * @param elem  the element this view is responsible for
  42.      */
  43.     public CompositeView(Element elem) {
  44.     super(elem);
  45.     children = new View[1];
  46.     nchildren = 0;
  47.  
  48.     }
  49.  
  50.     /**
  51.      * Loads all of the children to initialize the view.
  52.      * This is called by the <code>setParent</code> method.
  53.      * Subclasses can reimplement this to initialize their
  54.      * child views in a different manner.  The default
  55.      * implementation creates a child view for each 
  56.      * child element.
  57.      *
  58.      * @param f the view factory
  59.      */
  60.     protected void loadChildren(ViewFactory f) {
  61.     Element e = getElement();
  62.     int n = e.getElementCount();
  63.     if (n > 0) {
  64.         View[] added = new View[n];
  65.         for (int i = 0; i < n; i++) {
  66.         added[i] = f.create(e.getElement(i));
  67.         }
  68.         replace(0, 0, added);
  69.     }
  70.     }
  71.  
  72.     /**
  73.      * Removes all of the children.
  74.      */
  75.     public void removeAll() {
  76.     replace(0, nchildren, ZERO);
  77.     }
  78.  
  79.     /**
  80.      * Inserts a single child view.
  81.      *
  82.      * @param offs the offset of the view to insert before >= 0
  83.      * @param v the view
  84.      */
  85.     public void insert(int offs, View v) {
  86.     ONE[0] = v;
  87.     replace(offs, 0, ONE);
  88.     }
  89.  
  90.     /**
  91.      * Appends a single child view.
  92.      *
  93.      * @param v the view
  94.      */
  95.     public void append(View v) {
  96.     ONE[0] = v;
  97.     replace(nchildren, 0, ONE);
  98.     }
  99.  
  100.     /**
  101.      * Invalidates the layout and resizes the cache of requests/allocations,
  102.      * allowing for the replacement of child views.
  103.      *
  104.      * @param offset the starting offset into the child views to insert
  105.      *   before >= 0
  106.      * @param length the number of existing child views affected >= 0
  107.      * @param views the child views to use as replacements
  108.      */
  109.     public void replace(int offset, int length, View[] views) {
  110.     // update parent reference on removed views
  111.     for (int i = offset; i < offset + length; i++) {
  112.         children[i].setParent(null);
  113.     }
  114.     
  115.     // update the array
  116.     int delta = views.length - length;
  117.     int src = offset + length;
  118.     int nmove = nchildren - src;
  119.     int dest = src + delta;
  120.     if ((nchildren + delta) >= children.length) {
  121.         // need to grow the array
  122.         int newLength = Math.max(2*children.length, nchildren + delta);
  123.         View[] newChildren = new View[newLength];
  124.         System.arraycopy(children, 0, newChildren, 0, offset);
  125.         System.arraycopy(views, 0, newChildren, offset, views.length);
  126.         System.arraycopy(children, src, newChildren, dest, nmove);
  127.         children = newChildren;
  128.     } else {
  129.         // patch the existing array
  130.         System.arraycopy(children, src, children, dest, nmove);
  131.         System.arraycopy(views, 0, children, offset, views.length);
  132.     }
  133.     nchildren = nchildren + delta;
  134.  
  135.     // update parent reference on added views
  136.     for (int i = 0; i < views.length; i++) {
  137.         views[i].setParent(this);
  138.     }
  139.     }
  140.  
  141.     // --- View methods ---------------------------------------------
  142.  
  143.     /**
  144.      * Sets the parent of the view.
  145.      * This is reimplemented to provide the superclass
  146.      * behavior as well as calling the <code>loadChildren</code>
  147.      * method.  The children should not be loaded in the 
  148.      * constructor because the act of setting the parent
  149.      * may cause them to try to search up the hierarchy
  150.      * (to get the hosting Container for example).
  151.      *
  152.      * @param parent the parent of the view, null if none
  153.      */
  154.     public void setParent(View parent) {
  155.     super.setParent(parent);
  156.     if (parent != null) {
  157.         ViewFactory f = getViewFactory();
  158.         loadChildren(f);
  159.     }
  160.     }
  161.  
  162.     /** 
  163.      * Returns the number of views in this view.
  164.      *
  165.      * @return the number of views >= 0
  166.      * @see #getView
  167.      */
  168.     public int getViewCount() {
  169.     return nchildren;
  170.     }
  171.  
  172.     /** 
  173.      * Gets the n-th view in this container.
  174.      *
  175.      * @param n the number of the view to get, >= 0 && < getViewCount()
  176.      * @return the view
  177.      */
  178.     public View getView(int n) {
  179.     return children[n];
  180.     }
  181.  
  182.     /**
  183.      * Fetches the allocation for the given child view. 
  184.      * This enables finding out where various views
  185.      * are located, without assuming the views store
  186.      * their location.  
  187.      *
  188.      * @param index the index of the child, >= 0 && < getViewCount()
  189.      * @param a  the allocation to this view.
  190.      * @return the allocation to the child
  191.      */
  192.     public Shape getChildAllocation(int index, Shape a) {
  193.     Rectangle alloc = a.getBounds();
  194.     childAllocation(index, alloc);
  195.     return alloc;
  196.     }
  197.  
  198.     /**
  199.      * Provides a mapping from the document model coordinate space
  200.      * to the coordinate space of the view mapped to it.
  201.      *
  202.      * @param pos the position to convert >= 0
  203.      * @param a the allocated region to render into
  204.      * @return the bounding box of the given position
  205.      * @exception BadLocationException  if the given position does
  206.      *   not represent a valid location in the associated document
  207.      * @see View#modelToView
  208.      */
  209.     public Shape modelToView(int pos, Shape a) throws BadLocationException {
  210.     Rectangle alloc = getInsideAllocation(a);
  211.     View v = getViewAtPosition(pos, alloc);
  212.     if (v != null) {
  213.         int p0 = v.getStartOffset();
  214.         int p1 = v.getEndOffset();
  215.         if ((pos >= p0) && (pos < p1)) {
  216.         // get the childs idea of the coordinates
  217.         return v.modelToView(pos, alloc);
  218.         }
  219.     }
  220.     throw new BadLocationException("Position not represented by view", pos);
  221.     }
  222.  
  223.     /**
  224.      * Provides a mapping from the view coordinate space to the logical
  225.      * coordinate space of the model.
  226.      *
  227.      * @param x   x coordinate of the view location to convert >= 0
  228.      * @param y   y coordinate of the view location to convert >= 0
  229.      * @param a the allocated region to render into
  230.      * @return the location within the model that best represents the
  231.      *  given point in the view >= 0
  232.      * @see View#viewToModel
  233.      */
  234.     public int viewToModel(float x, float y, Shape a) {
  235.     Rectangle alloc = getInsideAllocation(a);
  236.     if (isBefore((int) x, (int) y, alloc)) {
  237.         // point is before the range represented
  238.         return getStartOffset();
  239.     } else if (isAfter((int) x, (int) y, alloc)) {
  240.         // point is after the range represented.
  241.         return getEndOffset() -1;
  242.     } else {
  243.         // locate the child and pass along the request
  244.         View v = getViewAtPoint((int) x, (int) y, alloc);
  245.         if (v != null) {
  246.           return v.viewToModel(x, y, alloc);
  247.         }
  248.     }
  249.     return -1;
  250.     }
  251.  
  252.     // --- local methods ----------------------------------------------------
  253.  
  254.  
  255.     /**
  256.      * Tests whether a point lies before the rectangle range.
  257.      *
  258.      * @param x the X coordinate >= 0
  259.      * @param y the Y coordinate >= 0
  260.      * @param alloc the rectangle
  261.      * @return true if the point is before the specified range
  262.      */
  263.     protected abstract boolean isBefore(int x, int y, Rectangle alloc);
  264.  
  265.     /**
  266.      * Tests whether a point lies after the rectangle range.
  267.      *
  268.      * @param x the X coordinate >= 0
  269.      * @param y the Y coordinate >= 0
  270.      * @param alloc the rectangle
  271.      * @return true if the point is after the specified range
  272.      */
  273.     protected abstract boolean isAfter(int x, int y, Rectangle alloc);
  274.  
  275.     /**
  276.      * Fetches the child view at the given point.
  277.      *
  278.      * @param x the X coordinate >= 0
  279.      * @param y the Y coordinate >= 0
  280.      * @param alloc the parent's allocation on entry, which should
  281.      *   be changed to the child's allocation on exit
  282.      * @return the child view
  283.      */
  284.     protected abstract View getViewAtPoint(int x, int y, Rectangle alloc);
  285.  
  286.     /**
  287.      * Returns the allocation for a given child.
  288.      *
  289.      * @param index the index of the child, >= 0 && < getViewCount()
  290.      * @param a  the allocation to the interior of the box on entry, 
  291.      *   and the allocation of the view containing the position on exit
  292.      */
  293.     protected abstract void childAllocation(int index, Rectangle a);
  294.  
  295.     /**
  296.      * Fetches the child view that represents the given position in
  297.      * the model.  This is implemented to fetch the view in the case
  298.      * where there is a child view for each child element.
  299.      *
  300.      * @param pos the position >= 0
  301.      * @param a  the allocation to the interior of the box on entry, 
  302.      *   and the allocation of the view containing the position on exit
  303.      * @returns  the view representing the given position, or 
  304.      *   null if there isn't one
  305.      */
  306.     protected View getViewAtPosition(int pos, Rectangle a) {
  307.     Element elem = getElement();
  308.     int index = elem.getElementIndex(pos);
  309.     Element child = elem.getElement(index);
  310.     if ((child != null) && (index < getViewCount())) {
  311.         View v = getView(index);
  312.         if (v.getElement() == child) {
  313.         if (a != null) {
  314.             childAllocation(index, a);
  315.         }
  316.         return v;
  317.         }
  318.     }
  319.     return null;
  320.     }
  321.  
  322.     /**
  323.      * Translates the allocation given to the view to the allocation used
  324.      * for composing the interior.  This takes into account any 
  325.      * margins that were specified.
  326.      *
  327.      * @param a The allocation given to the view.
  328.      * @returns The allocation that represents the inside of the 
  329.      *   view after the margins have all been removed.  If the
  330.      *   given allocation was null, the return value is null.
  331.      */
  332.     protected Rectangle getInsideAllocation(Shape a) {
  333.     if (a != null) {
  334.         Rectangle alloc = new Rectangle(a.getBounds());
  335.         alloc.x += left;
  336.         alloc.y += top;
  337.         alloc.width -= left + right;
  338.         alloc.height -= top + bottom;
  339.         return alloc;
  340.     }
  341.     return null;
  342.     }
  343.  
  344.     /**
  345.      * Sets the insets from the paragraph attributes specified in
  346.      * the given attributes.
  347.      *
  348.      * @param attr the attributes
  349.      */
  350.     protected final void setParagraphInsets(AttributeSet attr) {
  351.     // Since version 1.1 doesn't have scaling and assumes 
  352.     // a pixel is equal to a point, we just cast the point
  353.     // sizes to integers.
  354.     top = (short) StyleConstants.getSpaceAbove(attr);
  355.     left = (short) StyleConstants.getLeftIndent(attr);
  356.     bottom = (short) StyleConstants.getSpaceBelow(attr);
  357.     right = (short) StyleConstants.getRightIndent(attr);
  358.     }
  359.  
  360.     /**
  361.      * Sets the insets for the view.
  362.      *
  363.      * @param top the top inset >= 0
  364.      * @param left the left inset >= 0
  365.      * @param bottom the bottom inset >= 0
  366.      * @param right the right inset >= 0
  367.      */
  368.     protected final void setInsets(short top, short left, short bottom, short right) {
  369.     this.top = top;
  370.     this.left = left;
  371.     this.right = right;
  372.     this.bottom = bottom;
  373.     }
  374.  
  375.     /**
  376.      * Gets the left inset.
  377.      *
  378.      * @return the inset >= 0
  379.      */
  380.     protected final short getLeftInset() {
  381.     return left;
  382.     }
  383.  
  384.     /**
  385.      * Gets the right inset.
  386.      *
  387.      * @return the inset >= 0
  388.      */
  389.     protected final short getRightInset() {
  390.     return right;
  391.     }
  392.  
  393.     /**
  394.      * Gets the top inset.
  395.      *
  396.      * @return the inset >= 0
  397.      */
  398.     protected final short getTopInset() {
  399.     return top;
  400.     }
  401.  
  402.     /**
  403.      * Gets the bottom inset.
  404.      *
  405.      * @return the inset >= 0
  406.      */
  407.     protected final short getBottomInset() {
  408.     return bottom;
  409.     }
  410.  
  411.  
  412.     // ---- member variables ---------------------------------------------
  413.  
  414.     private static View[] ONE = new View[1];
  415.     private static View[] ZERO = new View[0];
  416.     
  417.     private View[] children;
  418.     private int nchildren;
  419.     private short left;
  420.     private short right;
  421.     private short top;
  422.     private short bottom;
  423. }
  424.